import { PBRShader } from "./js/shaders.js";
const canvas = document.getElementById("renderCanvas"); // Get the canvas element
let shadowGenerator;
let depthRenderer;
// 尝试获取 WebGL2 上下文
const gl = canvas.getContext("webgl2");
if (gl) {
  console.log("支持 WebGL 2.0");
} else {
  alert("设备不支持 WebGL 2.0");
}
// const vertexBuffer = new BABYLON.VertexBuffer()
const engine = new BABYLON.Engine(canvas, true, { alpha: true }); // Generate the BABYLON 3D engine
// engine.setAlphaMode(BABYLON.Engine.ALPHA_PREMULTIPLIED); // 设置透明模式
function showWorldAxis(size, scene) {
  const makeAxis = function (axis, color) {
    const axisMaterial = new BABYLON.StandardMaterial("axisMat", scene);
    axisMaterial.diffuseColor = color;
    axisMaterial.emissiveColor = color;

    const axisLine = BABYLON.MeshBuilder.CreateLines(
      "axis" + axis,
      {
        points: [
          BABYLON.Vector3.Zero(),
          axis === "X"
            ? new BABYLON.Vector3(size, 0, 0)
            : axis === "Y"
            ? new BABYLON.Vector3(0, size, 0)
            : new BABYLON.Vector3(0, 0, size),
        ],
      },
      scene
    );
    axisLine.color = color;
  };

  makeAxis("X", BABYLON.Color3.Red()); // 红色X轴
  makeAxis("Y", BABYLON.Color3.Green()); // 绿色Y轴
  makeAxis("Z", BABYLON.Color3.Blue()); // 蓝色Z轴
}
// 创建后处理
const createPostProcess = (camera) => {
  // 注册 shader 源码
  BABYLON.Effect.ShadersStore["customShaderVertexShader"] = /* glsl */ `
    precision highp float;
    // 必须有这些内置变量
    attribute vec2 position;
    varying vec2 vUV;

    void main(void) {
        vUV = (position + 1.0) * 0.5;
        gl_Position = vec4(position, 0.0, 1.0);
    }
`;

  BABYLON.Effect.ShadersStore["customShaderFragmentShader"] = /* glsl */ `
    precision highp float;
    varying vec2 vUV;
    uniform mat4 invViewProj;
    uniform mat4 shadowMatrix;
    uniform sampler2D textureSampler;
    uniform sampler2D shadowMapTextrue;
    uniform sampler2D depthMap;
    void main(void) {
      vec4 color = texture2D(textureSampler, vUV);
      float depth = texture2D(depthMap, vUV).x;
      float ndcZ = depth * 2.0 - 1.0;
      vec2 ndcXY = vUV * 2.0 - 1.0;  // 将[0,1]转换为[-1,1]
      vec4 worldPos = invViewProj * vec4(ndcXY, ndcZ, 1.0);
      worldPos /= worldPos.w;  // 透视除法

      // 1. 变换世界坐标到阴影贴图空间
      vec4 shadowCoord = shadowMatrix * worldPos;

      // 2. 透视除法
      shadowCoord /= shadowCoord.w;

      // 3. 从 [-1,1] 变换到 [0,1] UV 坐标
      vec2 uv = shadowCoord.xy;

      // 4. 检查 UV 是否有效（避免越界）
      if (uv.x < 0.0 || uv.x > 1.0 || uv.y < 0.0 || uv.y > 1.0) {
          // 可能不在阴影区域，直接输出不阴影
          gl_FragColor = vec4(1.0);
          return;
      }

      // 5. 采样阴影贴图
      float shadowDepth = texture2D(shadowMapTextrue, uv).r;
      // 6. 获取当前片元在阴影空间的深度
      float currentDepth = shadowCoord.z * 0.5 + 0.5;  // 也可能直接 shadowCoord.z
      // 7. 比较深度判断是否在阴影中
      float shadow = currentDepth > shadowDepth + 5e-4 ? 0.0 : 1.0;
      // 8. 输出颜色乘以阴影值
      gl_FragColor = vec4(color);
      // 示例: 输出深度值
      // gl_FragColor = vec4(vec3(color.rgb), 1.0);
      // gl_FragColor = vec4(vec3(color.rgb), 1.0);
    }
`;

  // 创建后处理
  const customPostProcess = new BABYLON.PostProcess(
    "customPostProcess",
    "customShader",
    ["shadowMatrix", "invViewProj"],
    ["textureSampler", "shadowMapTextrue", "depthMap"], // 屏幕颜色贴图 阴影贴图 深度贴图
    1.0,
    camera
  );

  const depthMap = depthRenderer.getDepthMap();
  // shadowGenerator.recreateShadowMap();
  const shadowMapTextrue = shadowGenerator.getShadowMap();
  console.log("shadowMapTextrue", shadowMapTextrue);
  const shadowMatrix = shadowGenerator.getTransformMatrix();
  const proj = camera.getTransformationMatrix();
  const invViewProj = proj.clone().invert(); // 注意 clone()，避免直接改原矩阵
  customPostProcess.onApply = (effect) => {
    effect.setTexture("shadowMapTextrue", shadowMapTextrue);
    effect.setTexture("depthMap", depthMap);
    effect.setMatrix("shadowMatrix", shadowMatrix);
    effect.setMatrix("invViewProj", invViewProj); // 传递给着色器,逆视图投影矩阵
  };
  return customPostProcess;
};
// 实时移动模型
const startMove = (scene, model) => {
  // let velocity = new BABYLON.Vector3(0, 0, 0); // 每秒前进 1 单位
  const myobserver = new BABYLON.Observable(); // 创建一个可观察者
  setInterval(() => {
    myobserver.notifyObservers((Math.random() - 0.5) * 2 * 5000); // 每秒通知观察者
  }, 1000);
  // scene.onBeforeRenderObservable.add(() => {
  //   const deltaTime = engine.getDeltaTime() / 1000;
  //   let nowplace = model.position;
  //   let u = setDamping(nowplace, velocity, deltaTime); // 计算阻尼
  //   model.position = model.position.add(u); // 让该物体相对移动
  // });
  myobserver.add((moveDistance) => {
    console.log("moveDistance", moveDistance);
    let velocity = new BABYLON.Vector3(moveDistance, 0, moveDistance);
    // 使用gsap缓动移动
    gsap.to(model.position, {
      duration: 1,
      x: velocity.x,
      y: velocity.y,
      z: velocity.z,
      ease: "power2.inOut",
    });
    console.log('model.position',model.position);
  });
};
const createScene = async function () {
  // Creates a basic Babylon Scene object
  const scene = new BABYLON.Scene(engine);
  scene.clearColor = new BABYLON.Color4(0, 0, 0, 0); // RGBA 最后一个参数是 alpha
  const camera = new BABYLON.ArcRotateCamera(
    "camera",
    -Math.PI / 2,
    Math.PI / 2.5,
    3,
    new BABYLON.Vector3(0, 0, 0),
    scene
  );
  // camera.upperBetaLimit = Math.PI / 2.1;    // 限制相机上下旋转角度
  // Targets the camera to scene origin
  camera.setTarget(BABYLON.Vector3.Zero());
  // This attaches the camera to the canvas
  camera.attachControl(canvas, true);

  // const camera = new BABYLON.FollowCamera(
  //   "FollowCam",
  //   new BABYLON.Vector3(0, 10, -10),
  //   scene
  // );
  const floor = BABYLON.MeshBuilder.CreateGround("ground", {
    width: 100,
    height: 100,
  });
  floor.position.y = -10.0;
  floor.checkCollisions = true;
  floor.receiveShadows = true;
  showWorldAxis(5, scene);
  // Move the sphere upward 1/2 its height
  // Creates a light, aiming 0,1,0 - to the sky
  // const light = new BABYLON.HemisphericLight(
  //   "light",
  //   new BABYLON.Vector3(0, 50, 0),
  //   scene
  // );
  // // Dim the light a small amount - 0 to 1
  // light.intensity = 1;
  // 平行光
  const light = new BABYLON.DirectionalLight(
    "light",
    new BABYLON.Vector3(0, -1, -1),
    scene
  );
  light.position = new BABYLON.Vector3(0, 100, 100);
  light.intensity = 1.0;
  // 环境光
  const ambientLight = new BABYLON.HemisphericLight(
    "hemi",
    new BABYLON.Vector3(0, 1, 0),
    scene
  );
  ambientLight.intensity = 0.3;

  // 阴影生成器
  shadowGenerator = new BABYLON.ShadowGenerator(1024, light);
  shadowGenerator.shadowMinZ = 10;
  shadowGenerator.shadowMaxZ = 100;
  shadowGenerator.usePercentageCloserFiltering = true;

  // shadowGenerator.usePoissonSampling = true;
  // 开启深度渲染器，得到深度贴图
  depthRenderer = scene.enableDepthRenderer(camera);
  const mesh = await loadModel(scene);
  console.log("mesh", mesh);
  startMove(scene, mesh);
  //The goal distance of camera from target
	// camera.radius = 30;
	
	// The goal height of camera above local origin (centre) of target
	// camera.heightOffset = 10;
	
	// // The goal rotation of camera around local origin (centre) of target in x y plane
	// camera.rotationOffset = 0;
	
	// //Acceleration of camera in moving from current to goal position
	// camera.cameraAcceleration = 0.01
	
	//The speed at which acceleration is halted 
	camera.maxCameraSpeed = 10
  camera.lockedTarget = mesh; //version 2.5 onwards
  camera.attachControl(canvas, true);
  createPostProcess(camera); // 后处理
  return scene;
};

// 加载模型
const loadModel = async function (scene) {
  let model1 = await BABYLON.ImportMeshAsync(
    "./models/scene.gltf",
    // "./models/wrench.gltf",
    // "./models/BoomBox.glb",
    // "./models/model.glb",
    scene
  ).then((result) => {
    console.log(result);
    let mesh = scene.getMeshByName("Frying_Pan_M_Pan_0"); // scene.getMeshByName("_wrench_hi_Volume18");
    if (mesh) {
      const hasTangents = mesh.isVerticesDataPresent(
        BABYLON.VertexBuffer.TangentKind
      );
      // console.log('tangent:', mesh.getVerticesData(BABYLON.VertexBuffer.TangentKind));
      console.log("是否包含切线:", hasTangents);
    }
    mesh.position = new BABYLON.Vector3(0, 0, 0);
    // mesh.rotate(BABYLON.Axis.X, Math.PI / 2, BABYLON.Space.LOCAL);
    mesh.scaling = new BABYLON.Vector3(50, 50, 50);
    // let pbrmat = mesh.material;
    // // 自定义PBR shader
    // const customMaterial = PBRShader(scene, "myshader");
    // // 传递 贴图
    // customMaterial.setTexture("albedoTexture", pbrmat.albedoTexture); // 基本颜色
    // customMaterial.setTexture("normalTexture", pbrmat.bumpTexture); // 法线贴图
    // customMaterial.setTexture(
    //   "metallicRoughnessTexture",
    //   pbrmat.metallicTexture
    // ); // 金属度和粗糙度贴图

    // 创建 PBR 材质
    const pbr = new BABYLON.PBRMaterial("pbrMat", scene);
    // 设置 BaseColor 贴图（albedoTexture）
    pbr.albedoTexture = new BABYLON.Texture(
      "./models/textures/Wall_3.056_14_baseColor.png",
      scene
    );
    // 这里假设是合成贴图
    pbr.metallicRoughnessTexture = new BABYLON.Texture(
      "./models/textures/Wall_3.056_14_metallicRoughness.png",
      scene
    );
    // 设置法线贴图
    pbr.bumpTexture = new BABYLON.Texture(
      "./models/textures/Wall_3.056_14_normal.jpeg",
      scene
    );
    let pbrmat = mesh.material;
    // 自定义PBR shader
    const customMaterial = PBRShader(scene, "myshader");
    // 传递 贴图
    customMaterial.setTexture("albedoTexture", pbr.albedoTexture); // 基本颜色
    customMaterial.setTexture("normalTexture", pbr.bumpTexture); // 法线贴图
    customMaterial.setTexture(
      "metallicRoughnessTexture",
      pbr.metallicRoughnessTexture
    ); // 金属度和粗糙度贴图
    mesh.material = customMaterial;
    // 投射阴影
    shadowGenerator.addShadowCaster(mesh);
    // mesh.receiveShadows = false;
    // shadowGenerator.getShadowMap().renderList.push(mesh);
    return Promise.resolve(mesh);
  });
  return model1;
  // startAnimation(model1);
};
const scene = await createScene(); //Call the createScene function
// Register a render loop to repeatedly render the scene
engine.runRenderLoop(function () {
  scene.render();
});
// Watch for browser/canvas resize events
window.addEventListener("resize", function () {
  engine.resize();
});
